Skip to content

Conversation

@bgrainger
Copy link
Member

Issue #178 suggests that the connection be reset when it's returned to the pool, so that it's ready immediately when a new MySqlConnection is opened (and reuses a pooled connection).

In the current PR, the connection is reset synchronously when the session is closed; this may not be sufficient to increase performance since the caller will still be blocked (albeit at the end of the connection lifetime, not at the beginning). It may be necessary to offload the work of resetting the connection to a background thread.

The method completes synchronously, so the waiting isn't strictly necessary. However, the new code will rethrow any exception that the returned Task may have captured, which is useful for correctness (not that Dispose should throw an exception). Additionally, a call to `ConfigureAwait` without an `await` is confusing; this new code now uses the "standard" pattern for synchronously calling an async method.
@caleblloyd
Copy link
Contributor

This is an interesting proposition. I'll do some review and performance testing on it tomorrow.

@caleblloyd
Copy link
Contributor

caleblloyd commented Feb 16, 2017

Use of Synchronous I/O in ConnectionPool.Return is correct. Switching the Connection Reset to Async causes lock-ups when using Sync I/O.

I think that awaiting the result of the Connection Reset should be deferred until the connection is drawn from the pool again. Since connections are inserted at the back of the Queue, this should give it a nice amount of time to complete before it is drawn again.

While we're at it - the TryPingAsync command really only needs to be run after a certain amount of idle time (say 30 seconds). If the connection has been active in a short period of time, this is pretty much a wasted round trip. (I'll address this in a separate issue)

I pushed a branch with a commit on top of this PR that has this behavior: 5313c56 you should be able to cherry-pick that commit into this PR if it looks good.

@caleblloyd
Copy link
Contributor

caleblloyd commented Feb 16, 2017

I think this will unintentionally fix #169 because the exception in ResetConnectionAsync will get caught and the MySqlSession will be disposed. It will mask that exception every time, I'm not sure if that's a good thing because the user may think pooling is working when it is actually not working. Not sure of the best way to handle though.

@bgrainger
Copy link
Member Author

As I wrote on #178:

The downside would be that any errors would be silently ignored and only manifest themselves as a performance problem (because the pool would never contain live connections).

I also don't currently have a good idea for handling this.

Were you able to do any performance testing on this yet, to see if the change is worthwhile?

@caleblloyd
Copy link
Contributor

caleblloyd commented Feb 16, 2017

I also don't currently have a good idea for handling this.

If we want to mirror the current behavior, we could still initiate the reset task upon returning, but check for an exception upon re-drawing from the pool. It would essentially be removing this try-catch

Were you able to do any performance testing on this yet, to see if the change is worthwhile?

The main benefit here would be for clients which exhibit behaviors:

(A) Connection pool is not constantly in use
(B) There is a high Round Trip Time (RTT) between Client and Server

Looking at our current traffic for drawing from the connection pool, we have 3 round trips:

image

RT 1 is from PingSessionAsync. This takes RTT + 0.07ms
RT 2 is from actual resetting of the connection. This takes RTT + 0.06ms
RT 3 is from SET NAMES utf8mb4. This takes RTT + 0.06ms

If the connection pool is constantly in use, there will be no difference whether connection is reset at the beginning or end because those events will occur back-to-back. If there is a negligible RTT (e.g. localhost like my timings here), then all of this will happen in a fraction of a millisecond. But, if (A) and (B) are both true, then we will move 2 of the 3 Round Trips when leasing a connection to a time when the connection would normally be idle, and that will benefit clients with a high RTT.

One thing to consider is that by implementing this, we would now be forcing these 2 round trips over Sync I/O, which will block a thread pool thread. I don't think we force Sync I/O on any other happy paths in the codebase currently.

Another option is to run ResetConnectionAsync in the place of PingSessionAsync, and catch a MySqlException in the case that the connection was closed by the server. This would eliminate one round trip.

@caleblloyd
Copy link
Contributor

caleblloyd commented Feb 20, 2017

As a follow-up to my most recent comment:

One thing to consider is that by implementing this, we would now be forcing these 2 round trips over Sync I/O, which will block a thread pool thread. I don't think we force Sync I/O on any other happy paths in the codebase currently.

After reflecting on this a little more, I think that staying 100% async on the happy path is a primary goal of our library and we should not move forward with this change.

Another option is to run ResetConnectionAsync in the place of PingSessionAsync, and catch a MySqlException in the case that the connection was closed by the server. This would eliminate one round trip.

This is still an option but would mask errors in ResetConnectionAsync by catching the exception. It would have to be done by parsing exception messages. If everyone else agrees, my vote is to abandon this PR and open this change as an "up for grabs" issue.

@bgrainger
Copy link
Member Author

Closing in favour of #264.

@bgrainger bgrainger closed this May 16, 2017
@bgrainger bgrainger deleted the reset-connection-on-return branch May 16, 2017 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants